package Dominio;

import Presentacion.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Random;
/**
 *
 * @author Victor Varias Perez @ Aleix Fernandez Donis
 * Cluster y grupo: C8G3
 * Creado el 16-may-2010 13:19:03
 *
 * Objetivo de la clase: Controlar las funcionalidades de la partida.
 * Clases utilizadas: Carta, Mazo, Jugador, GUIRemigio
 */
/*

 * particion correcta:
 *  escaleras han de ir de abajo parriba
 */

public class PartidaRemigio extends Partida {
    /**
     * es para controlar cuando volvemos de una partición si la habiamos llamado
     * desde jugar o desde actualizar
     * true juega, false actualiza
     */
    boolean juegaoactualiza;
    boolean haterminado;
    public GUIJugador guij;
    /**
     * lo usamos para actualizarPuntosS
     */
    int jugadorvisto;
    Mazo descarte;
    int juegostotales;
    Reglas reglas;
    int num_mazos;
    boolean comodin;
    GUIRemigio interficie;
    Random rand;
    boolean cerrar;
    Jugada jd0, jd1;
    Carta c0 = null;

    //@Victor
    public PartidaRemigio (int num_jugadores, Reglas reglas, GUIRemigio interficie) {
    //public PartidaRemigio (int num_jugadores, Reglas reglas) {
        super(num_jugadores, (int) (reglas.getBarajaxjugador() * (double)num_jugadores), reglas.getComodin());
        this.num_mazos = (int) (reglas.getBarajaxjugador() * (double)num_jugadores);
        this.comodin = reglas.getComodin();
        juegostotales = 0;
        descarte = new Mazo(0, true);
        this.reglas = reglas;
        Date date = new Date();
        rand = new Random(date.getTime());
        this.interficie = interficie;
    }


    public void AumentaJuegostotales() {
        juegostotales += 1;
    }


    public int GetJuegostotales() {
        return juegostotales;
    }


    public JugadorRemigio VerJugador(int i) {
        return (JugadorRemigio) super.jugadores[i];
    }


    public void DejarDescarte(Carta c) {
        this.descarte.introducir_carta(1, c);
    }

    public Carta CogerDescarte() {
        return this.descarte.sacar_carta();
    }

    public Carta ConsultarDescarte () {
      /* versio victor, que no ha d'anar així
        Carta c = this.descarte.consultar_carta(1);
        this.descarte.introducir_carta(1, c);
        return c;
       *
       */
      return descarte.consultar_carta(1);
    }

    public Carta CogerCartaBaraja() {
        if (baraja.mazo_vacio()) {
          Mazo tmp = new Mazo(0, true);
          tmp.introducir_carta(1, CogerDescarte());
          baraja = descarte;
          descarte = tmp;
          baraja.barajar();
        }
        Carta c = baraja.sacar_carta();
        return c;
    }

    //@Victor
    public void EmpezarPartida() {
      //jugadores tienen puntos a 0...
      //int m = 0;
      if (!Fin()){
          NuevoJuego();
      }
      else{
          //public void showganador(String ganador){
          if (this.VerJugador(this.getJugadorActual()).EsHumano()){
              interficie.showganador(this.VerJugador(this.getJugadorActual()).GetUser().getLogin());
          }
          else{
              interficie.showganador("Maquina");
          }

      }
    }

    //@Aleix
    public void NuevoJuego() {
      IniciarJuego();
      int[] puntuaciones = new int[getNumJugadores()];
      for (int i = 0; i < getNumJugadores(); ++i) {
        puntuaciones[i] = VerJugador(i).GetPuntos();
      }
      for (int i = 0; i < getNumJugadores(); ++i) {
        JugadorRemigio j = VerJugador(i);
        if (!j.EsHumano()) {
          j.GetIE().repartir(i, j.DevolverMano(), puntuaciones, reglas);
        }
      }
      cerrar = false;
      jugar(0);
    }

    //@Victor&Aleix
    public void jugar(int i){
      /**
       * @param i:
       * nos dice la accion que le toca realizar al jugador en caso de ser humano
       * 0 = no ha hecho nada
       * 1 = ha hecho elegir
       * 2 = ha hecho getJugada
       * 3 = ha realizado la accion(baraja o descarte)
       * 4 = ha hecho juega
       * 5 = ha hecho getJugada
       * 6 = ha realizado la accion(descartar o cerrar)
       */
        juegaoactualiza = true;
      while (true) {
        int ja = getJugadorActual();
        jd0 = new Jugada(2);
        jd1 = new Jugada(2);
        JugadorRemigio j = VerJugador(ja);
        Mazo mano = j.DevolverMano();
        //baraja o descarte
        if (j.EsHumano()) {
            if (i == 0){
                c0 = ConsultarDescarte();
                interficie.elegir(mano, ConsultarDescarte(), ja);
            }
            else if (i == 1){
                if (interficie.ha_jugado()){
                jd0 = interficie.getJugada();
                i++;
                }
            }
        }
        else {
          c0 = ConsultarDescarte();
          jd0 = j.GetIE().elegir(mano, ConsultarDescarte());
          i=2;
        }
        //realizar acción pedida por el jugador...
        if (i == 2){
            if (jd0.getAccion() == Jugada.BARAJA) {
              j.CogerMas(CogerCartaBaraja());
            }
            else {
            j.CogerMas(CogerDescarte());
            }
            i++;
        }
        //cerar o descartar
        if (j.EsHumano()) {
            if (i == 3){
                interficie.juega(mano, ja);
            }
            else if (i == 4){
                if (interficie.ha_jugado()){
                    jd1 = interficie.getJugada();
                    i++;
                }
            }
        }
        else {
          jd1 = j.GetIE().juega(mano);
          i=5;
        }
        //realiazar acción
        if (i == 5){
            if (jd1.getAccion() == Jugada.CERRAR) {
                if (!cerrarMano(jd1.getParticion())) {
                    //si no nos da partición correcta jugamos aleatoriamente...
                    DejarDescarte(j.Descartar(posicionAleatoria()));
                }
                else {
                    cerrar = true;
                }
                i++;
            }
            else {
              if (!enRango11(jd1.getPosicion())) {
                //ídem
                DejarDescarte(j.Descartar(posicionAleatoria()));
              }
              else {
                DejarDescarte(j.Descartar(jd1.getPosicion()));
              }
              i++;
            }
        }
        for (int m = 0; m < getNumJugadores(); ++m) {
            JugadorRemigio t = VerJugador(m);
            if (!t.EsHumano()) {
                t.GetIE().accion(getJugadorActual(), c0, jd0.getAccion(), ConsultarDescarte());
            }
        }

        if (cerrar) {
          ActualizarPuntos(0,0);
          AumentaJuegostotales();
          EmpezarPartida();
          break;
        }

        if (i == 6){
            finronda();
            break;
        }
        break;
      }
    }

    public void finronda(){
        this.CalcularSiguienteJugador();
        jugar(0);
    }

    public void juega_actualiza(int i){
        if (juegaoactualiza){
            jugar(4);
        }
        else{
            ActualizarPuntos(1, jugadorvisto ++);
        }
    }
    //@Aleix
    public void ActualizarPuntos(int control, int jugadorsiguiente) {
      int np;
      int mul = 1;
      jugadorvisto = jugadorsiguiente;
      JugadorRemigio j = VerJugador(jugadorvisto);
      if (control == 0){
          juegaoactualiza = false;
          mul = 1;//ESTO LO TIENE EL ACTUAL!
          jugadorvisto = 0;
          j = VerJugador(jugadorvisto);
      }
      if (control == 1){
          while (jugadorvisto<getNumJugadores()){
              if (jugadorvisto != getJugadorActual()) {
                  j = VerJugador(jugadorvisto);
                  if (!j.EsHumano()) {
                    np = evaluarManoN(j.GetIE().particion(j.DevolverMano()).getParticion(), j.DevolverMano());
                    j.SetPuntos(np*mul+j.GetPuntos());
                    jugadorvisto++;
                  }
                  else {
                    interficie.particion(j.DevolverMano(), jugadorvisto);
                    break;
                  }
              }
          }
      }
      if (control == 2){
          np = evaluarManoN(interficie.getJugada().getParticion(), j.DevolverMano());
          j.SetPuntos(np*mul+j.GetPuntos());
          ActualizarPuntos(1, jugadorvisto ++);
      }
    }
       /*

          for (int i = 0; i < getNumJugadores(); ++i) {
            if (i != ja) {
              JugadorRemigio j = VerJugador(i);
              int np;
              if (!j.EsHumano()) {
                np = evaluarManoN(j.GetIE().particion(j.DevolverMano()).getParticion(), j.DevolverMano());
              }
              else {
                np = evaluarManoN(interficie.particion(j.DevolverMano(), i).getParticion(), j.DevolverMano());
              }
              j.SetPuntos(np*mul+j.GetPuntos());
            }
          }
      }
      
    }*/

    //@Aleix
    public boolean Fin() {
      int m = 1;
      int mp = VerJugador(0).GetPuntos();
      int p = reglas.getLimitepuntos();
      int c = mp < p ? 1 : 0;
      for (int i = 1; i < getNumJugadores(); ++i) {
        int pi = VerJugador(i).GetPuntos();
        if (pi < mp) {
          m = i;
          mp = pi;
        }
        if (pi < p) {
          ++c;
        }
      }


      if ((reglas.getLimitepuntos() > 0 && c <= 1)
          || (reglas.getLimiteJuegos() > 0 && juegostotales > reglas.getLimiteJuegos())) {
          JugadorRemigio j = VerJugador(m);
          j.SetGanador(true);
          return true;
      }

      return false;
    }

    //@Aleix
    public void IniciarJuego() {
      InicializarMazo(num_mazos, comodin);
      baraja.barajar();
      for (int i = 0; i < getNumJugadores(); ++i) {
        JugadorRemigio j = VerJugador(i);
        for (int k = 0; k < 10; ++k) {
          j.Coger(CogerCartaBaraja());
        }
      }
      descarte = new Mazo(0, comodin);
      descarte.introducir_carta(1, CogerCartaBaraja());
    }

    //@Aleix
    public int evaluarManoN(ArrayList<int[]> p, Mazo mano) {
      boolean[] ocupadas = new boolean[12];//inicializa a false
      int t = 0;
      if (particionCorrecta(p, mano) > 0) {
        Iterator<int[]> it = p.iterator();
        while (it.hasNext()) {
          int[] fst = it.next();
          for (int i = 0; i < fst.length; ++i) {
            ocupadas[fst[i]] = true;
          }
        }
      }
      for (int i = 1; i <= 11; ++i) {
        if (ocupadas[i]) {
          t += valor(mano.consultar_carta(i));
        }
      }
      return t;
    }

    //-1 = partició dolenta
    //i = num cartes

    //@Aleix
    public int particionCorrecta(ArrayList<int[]> p, Mazo mano) {
      if (p == null) return 0;
      Iterator<int[]> it = p.iterator();



      //no es vacÃ­o
      if (!it.hasNext()) {
        System.err.println("vacio");
        return 0;
      }


      int[] utilizadas = new int[12];//inicia a 0... CUIDADO POSICIÃ“N 0 NO SE USA, POR COMODIDAD...
      int[] fst = it.next();

      if(fst.length == 2) {
        //opcion cinco parejas
        if (!reglas.getCincoparejas()) {
          System.err.println("intentar parejas sin cinco parejas en reglas");
          return 0;
        }
        if (p.size() != 5) {
          System.err.println("no hay cinco parejas");
          return 0;
        }
        it = p.iterator();
        while (it.hasNext()) {
          fst = it.next();
          //ha de ser grupo de dos cartas
          if (fst.length != 2) {
            System.err.println("mal tamaño");
            return 0;
          }
          //en rango y no utilizadas..
          if (!enRango11(fst[0]) || utilizadas[fst[0]] == 1) {
            System.err.println("mal rango " + fst[0]);
            return 0;
          }
          else {
            utilizadas[fst[0]] = 1;
          }
          if (!enRango11(fst[1]) || utilizadas[fst[1]] == 1) {
            System.err.println("mal rango " + fst[1]);
            return 0;
          }
          else {
            utilizadas[fst[0]] = 1;
          }

          Carta c1 = mano.consultar_carta(fst[0]);
          Carta c2 = mano.consultar_carta(fst[1]);
          if (!mismoValor(c1, c2)) {
            System.err.println("no son parejas");
            return 0;
          }
        }
        return 10;
      }
      else {
        it = p.iterator();
        int c = 0;
        while (it.hasNext()) {
          fst = it.next();
          if (fst.length < 3 || fst.length > 10) {
            System.err.println("mal tamaño");
            return 0;
          }
          for (int i = 0; i < fst.length; ++i) {
            if (!enRango11(fst[i]) || utilizadas[fst[i]] == 1) {
              System.err.println("mal rango " + fst[i]);
              return 0;
            }
            else {
              utilizadas[fst[i]] = 1;
            }
          }

          //pillamos como base el primer no comodin
          int b = 0;
          while (b < fst.length && comodin(mano.consultar_carta(fst[b]))) {
            b++;
          }

          c += fst.length;//sumamos las cartas, si este conjunto no es vÃ¡lido tampoco importa...

          if (b + 1 >= fst.length) { //si todas, o todas menos una son comodines, es un conjunto vÃ¡lido
            break;//QUEDA FEILLO
          }

          Carta c1 = mano.consultar_carta(fst[b]);
          Carta c2 = mano.consultar_carta(fst[b+1]);
          if (mismoValor(c1, c2)) {
            //todas del mismo valor...
            for (int i = b+2; i < fst.length; ++i) {
              c2 = mano.consultar_carta(fst[i]);
              if (!mismoValor(c1, c2)) {
                System.err.println("un grupo que hay una carta sin mismo valor " + i);
                return 0;
              }
            }
          }
          else {
            //escalera... MIRAR PORQUE QUIZÁS HAY ERROR
            for (int i = b+1; i < fst.length; ++i) {
              c2 = mano.consultar_carta(fst[i]);
              if (!mismoValor(c1, c2, i-b)) {
                System.err.println("una escalera con carta inválida " + i);
                return 0;
              }
            }
          }
        }
        return c;
      }
    }


    /**
     * comprovamos que una partición (que puede estar mal definidia, por eso de que idealmente es cliente-servidor y no hay manera de asegurar que el cliente no es malévolo)
     * sea correcta y que pueda cerrar
     */

    //@Aleix
    public boolean cerrarMano(ArrayList<int[]> p) {
      Mazo mano = VerJugador(getJugadorActual()).DevolverMano();
      return particionCorrecta(p, mano) == 10;
    }


    boolean enRango11(int j) {
      return 1 <= j && j <= 11;
    }


    int sumM13(int j, int i) {
      //1 <= j <= 12, 1 <= i <= 10
      j += i;
      if (j > 13) j -= 13;
      return j;
    }


   //FUNCIONES QUE DEBERÍAN ESTAR EN CARTAS, PERO QUE NO ESTAn...
   boolean mismoValor(Carta c1, Carta c2) {
     return comodin(c1) || comodin(c2) || c1.getValor() == c2.getValor();
   }

   boolean mismoValor(Carta c1, Carta c2, int i) {
     return comodin(c1) || comodin(c2) || sumM13(c1.getValor(), i) == c2.getValor();
   }

   boolean mismoPalo(Carta c1, Carta c2) {
     return comodin(c1) || comodin(c2) || c1.getPalo().equals(c2.getPalo());
   }

   boolean comodin(Carta c) {
     return c.getPalo().equals("Comodin");
   }

   int valor(Carta c) {
     return comodin(c) ? reglas.getValorComodin() : c.getValor();
   }

   int posicionAleatoria() {
       //post: 1 <= pA <= 11
       return rand.nextInt(11) + 1;
   }

}